<!DOCTYPE HTML>
<html lang="zh-CN">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="响应式系统中的背压, 码道诚公">
    <meta name="description" content="">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="baidu-site-verification" content="code-oqVlgh2wQV" />
    <meta name="google-site-verification" content="Z-8Fm46WY6NL_3kxilmoqToW4jnvyF7boTgciTLBXUs" />
    <!-- Global site tag (gtag.js) - Google Analytics -->


    <title>响应式系统中的背压 | 码道诚公</title>
    <link rel="icon" type="image/png" href="/favicon.png">

    <link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.css">
    <link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/css/my.css">

    <script src="/libs/jquery/jquery.min.js"></script>

<meta name="generator" content="Hexo 5.2.0">
<style>.github-emoji { position: relative; display: inline-block; width: 1.2em; min-height: 1.2em; overflow: hidden; vertical-align: top; color: transparent; }  .github-emoji > span { position: relative; z-index: 10; }  .github-emoji img, .github-emoji .fancybox { margin: 0 !important; padding: 0 !important; border: none !important; outline: none !important; text-decoration: none !important; user-select: none !important; cursor: auto !important; }  .github-emoji img { height: 1.2em !important; width: 1.2em !important; position: absolute !important; left: 50% !important; top: 50% !important; transform: translate(-50%, -50%) !important; user-select: none !important; cursor: auto !important; } .github-emoji-fallback { color: inherit; } .github-emoji-fallback img { opacity: 0 !important; }</style>
<link rel="alternate" href="/atom.xml" title="码道诚公" type="application/atom+xml">
<link rel="stylesheet" href="/css/prism-tomorrow.css" type="text/css"></head>




<body>
    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper container">
            <div class="brand-logo">
                <a href="/" class="waves-effect waves-light">
                    
                    <img src="/medias/logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">码道诚公</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>首页</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/tags" class="waves-effect waves-light">
      
      <i class="fas fa-tags" style="zoom: 0.6;"></i>
      
      <span>标签</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/categories" class="waves-effect waves-light">
      
      <i class="fas fa-bookmark" style="zoom: 0.6;"></i>
      
      <span>分类</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/archives" class="waves-effect waves-light">
      
      <i class="fas fa-archive" style="zoom: 0.6;"></i>
      
      <span>归档</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/about" class="waves-effect waves-light">
      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>关于</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/contact" class="waves-effect waves-light">
      
      <i class="fas fa-comments" style="zoom: 0.6;"></i>
      
      <span>留言板</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/friends" class="waves-effect waves-light">
      
      <i class="fas fa-address-book" style="zoom: 0.6;"></i>
      
      <span>友情链接</span>
    </a>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="搜索" style="zoom: 0.85;"></i>
    </a>
  </li>
</ul>


<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
        <img src="/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">码道诚公</div>
        <div class="logo-desc">
            
            Never really desperate, only the lost of the soul.
            
        </div>
    </div>

    

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			首页
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/tags" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-tags"></i>
			
			标签
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/categories" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-bookmark"></i>
			
			分类
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/archives" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-archive"></i>
			
			归档
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/about" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-user-circle"></i>
			
			关于
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/contact" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-comments"></i>
			
			留言板
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/friends" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-address-book"></i>
			
			友情链接
		</a>
          
        </li>
        
        
    </ul>
</div>


        </div>

        
    </nav>

</header>

    



<div class="bg-cover pd-header post-cover" style="background-image: url('/medias/featureimages/14.jpg')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <h1 class="description center-align post-title">响应式系统中的背压</h1>
                </div>
            </div>
        </div>
    </div>
</div>




<main class="container content">

    
    <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                    <div class="article-tag">
                        
                            <a href="/tags/Java-Stream/">
                                <span class="chip bg-color">Java Stream</span>
                            </a>
                        
                            <a href="/tags/Reactive-Stream/">
                                <span class="chip bg-color">Reactive Stream</span>
                            </a>
                        
                            <a href="/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/">
                                <span class="chip bg-color">响应式编程</span>
                            </a>
                        
                    </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                    <div class="post-cate">
                        <i class="fas fa-bookmark fa-fw icon-category"></i>
                        
                            <a href="/categories/Java/" class="post-category">
                                Java
                            </a>
                        
                            <a href="/categories/Java/Reactive-Stream/" class="post-category">
                                Reactive Stream
                            </a>
                        
                            <a href="/categories/Java/Reactive-Stream/%E5%93%8D%E5%BA%94%E5%BC%8F/" class="post-category">
                                响应式
                            </a>
                        
                    </div>
                    
                </div>
            </div>

            <div class="post-info">
                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-minus fa-fw"></i>发布日期:&nbsp;&nbsp;
                    2021-04-20
                </div>
                

                

                
                <div class="info-break-policy">
                    <i class="far fa-file-word fa-fw"></i>文章字数:&nbsp;&nbsp;
                    1.3k
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-clock fa-fw"></i>阅读时长:&nbsp;&nbsp;
                    5 分
                </div>
                

                
            </div>
        </div>
        <hr class="clearfix">
        <div class="card-content article-card-content">
            <div id="articleContent">
                <p>这篇文章是关于背压的简单介绍，并讲述了RxJava (v3)、Project Reactor和Kotlin的协程中是如何处理它的。</p>
<blockquote>
<p>Back pressure (or backpressure) is a resistance or force opposing the desired flow of fluid through pipes, leading to friction loss and pressure drop.</p>
<p>The term back pressure is a misnomer, as pressure is a scalar quantity, so it has a magnitude but no direction.</p>
<p>后向压力（背压）是一种阻力或力，它与通过管道的期望流体流量相反，从而导致摩擦损失和压降。</p>
<p>这个术语是一个误称，因为压力是一个标量，所以它只有大小没有方向。</p>
<p>​                                                                                                                                                                    ——<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Back_pressure">Wikipedia</a></p>
</blockquote>
<p>在软件中，背压的含义略有关联，但仍有不同：假设有一个快的数据生产者和一个慢的数据消费者，背压就是一种 “倒逼 “生产者而使自己不被数据淹没的机制。</p>
<p>无论是基于<a target="_blank" rel="noopener" href="https://www.reactive-streams.org/">reactstreams.org</a>还是Java的<code>java.util.concurrent.Flow</code>，Reactive Streams都提供了四个构件：</p>
<ol>
<li>一个产生元素的<code>Publisher</code></li>
<li>一个接收到元素时做出反应的<code>Subscriber</code></li>
<li>一个绑定<code>Publisher</code>和<code>Subscriber</code>的 <code>Subscription</code></li>
<li>以及一个<code>Processor</code></li>
</ol>
<p>下面是一个类图：</p>
<p><img src="/posts/dc1362d2/backpressure-reactive-systems-8897292.svg" alt="Reactive Streams class diagram"></p>
<p><code>Subscription</code>通过其中的<code>request()</code>方法成为背压的根源。</p>
<p>规范非常直白：</p>
<blockquote>
<p>A <code>Subscriber</code> MUST signal demand via <code>Subscription.request(long n)</code> to receive <code>onNext</code> signals.</p>
<p>The intent of this rule is to establish that it is the responsibility of the Subscriber to decide when and how many elements it is able and willing to receive. To avoid signal reordering caused by reentrant Subscription methods, it is strongly RECOMMENDED for synchronous Subscriber implementations to invoke Subscription methods at the very end of any signal processing. It is RECOMMENDED that Subscribers request the upper limit of what they are able to process, as requesting only one element at a time results in an inherently inefficient “stop-and-wait” protocol.</p>
<p><code>Subscriber</code> 必须通过<code>Subscription.request(long n)</code>发出信号请求，以接收<code>onNext</code>信号。</p>
<p>这条规则的目的是确立订阅者有责任决定何时以及它能够与愿意接收多少元素。为了避免重入订阅方法引起的信号重排序，强烈<strong>建议</strong>同步订阅者在任何信号处理的最后阶段调用Subscription方法。<strong>建议</strong>订阅者按照能够处理的上限请求数据，因为一次只请求一个元素会导致低效的 “停止-等待 “协议。</p>
<p>​                                                                        — <a target="_blank" rel="noopener" href="https://github.com/reactive-streams/reactive-streams-jvm">Reactive Streams specifications for the JVM(JVM的Reactive Streams规范)</a></p>
</blockquote>
<p>Reactive Streams（反应流）的规范非常可靠。它们还附带了基于java的TCK。</p>
<p>但如何管理由生产者发出的、不能被下游处理的项目，这不属于规范的范畴。虽然问题相当简单，但也可以有不同的解决方案。每个Reactive框架都提供了一些选项，让我们依次来看看。</p>
<h2 id="RxJava-3中的背压"><a href="#RxJava-3中的背压" class="headerlink" title="RxJava 3中的背压"></a>RxJava 3中的背压</h2><p>RxJava v3中提供了一些基本类：</p>
<table>
<thead>
<tr>
<th>类</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>Flowable</td>
<td>包含0到N个项的流，支持Reactive-Streams和背压</td>
</tr>
<tr>
<td>Observable</td>
<td>包含0到N个项的流，不支持背压</td>
</tr>
<tr>
<td>Single</td>
<td>只包含一个项、或者一个错误的流</td>
</tr>
<tr>
<td>Maybe</td>
<td>不包含项、只包含一个项，或者包含一个错误的流</td>
</tr>
<tr>
<td>Completable</td>
<td>不包含项，但是要么包含一个结束符、或者一个错误信号的流</td>
</tr>
</tbody></table>
<p>在这些类中，<code>Flowable</code>是唯一实现Reactive Streams和背压的类。 但是，提供背压并不是唯一的问题。 正如RxJava的Wiki所述：</p>
<blockquote>
<p><em>Backpressure doesn’t make the problem of an overproducing Observable or an underconsuming Subscriber go away. It just moves the problem up the chain of operators to a point where it can be handled better.</em></p>
<p>背压并不能解决Observable过度生产或Subscriber消费不足的问题。它只是将问题在操作者的链条中向上移动，使其能够得到更好的处理。</p>
<p>​                                                                                                                            <em>—</em> <a target="_blank" rel="noopener" href="https://github.com/ReactiveX/RxJava/blob/3.x/docs/Backpressure.md#reactive-pull-backpressure-isnt-magic">Reactive pull backpressure isn’t magic</a></p>
</blockquote>
<p>为了解决这个问题，RxJava提供了两种主要策略来处理“过度生产”的项：</p>
<ol>
<li><p>将项存储在缓冲器中</p>
<p><img src="/posts/dc1362d2/bp.obp.buffer.v3-8897292.png" alt="img"></p>
<p>请注意，如果你没有为缓冲区设置上限，则可能会导致<code>OutOfMemoryError</code>。</p>
</li>
<li><p>丢弃项</p>
<p><img src="/posts/dc1362d2/bp.obp.drop.v3-8897292.png" alt="img"></p>
</li>
</ol>
<p>下图总结了实现这些策略的不同方法：</p>
<p><img src="/posts/dc1362d2/rx-java-flowable-8897292.svg" alt="RxJava’s Flowable class diagram excerpt"></p>
<p>请注意，<code>onBackPressureLatest</code>操作类似于使用<code>onBackpressureBuffer(1)</code>：</p>
<p><img src="/posts/dc1362d2/bp.obp.latest.v3-8897292.png" alt="img"></p>
<p>与其它框架相比，RxJava提供了在发送所有项之后发送溢出异常信号的方法。这些方法可以让消费者在接收数据项的同时，仍然得到生产者丢弃数据项的通知。</p>
<h2 id="Project-Reactor中的背压"><a href="#Project-Reactor中的背压" class="headerlink" title="Project Reactor中的背压"></a>Project Reactor中的背压</h2><p>Project Reactor提供的策略与RxJava的类似。</p>
<p>不过API有一些细微的差别。例如，Project Reactor提供了一个方便的方法，可以在生产者溢出时抛出一个异常：</p>
<pre class=" language-java"><code class="language-java">var stream <span class="token operator">=</span> Stream<span class="token punctuation">.</span><span class="token function">generate</span><span class="token punctuation">(</span>Math<span class="token operator">:</span><span class="token operator">:</span>random<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment" spellcheck="true">// RxJava</span>
Flowable<span class="token punctuation">.</span><span class="token function">fromStream</span><span class="token punctuation">(</span>stream<span class="token punctuation">)</span>        
        <span class="token punctuation">.</span><span class="token function">onBackpressureBuffer</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   

<span class="token comment" spellcheck="true">// Project Reactor</span>
Flux<span class="token punctuation">.</span><span class="token function">fromStream</span><span class="token punctuation">(</span>stream<span class="token punctuation">)</span>            <span class="token comment" spellcheck="true">// 1</span>
    <span class="token punctuation">.</span><span class="token function">onBackpressureError</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment" spellcheck="true">// 2</span></code></pre>
<ol>
<li>创建Reactive Stream</li>
<li>如果生产者溢出则抛出错误</li>
</ol>
<p>下面是<code>Flux</code>类图，重点介绍背压能力：</p>
<p><img src="/posts/dc1362d2/reactor-flux-8897292.svg" alt="Project Reactor’s Flux class diagram excerpt"></p>
<p>与其他框架相比，Project Reactor提供了方法为缓冲区的数据项设置TTL，以防止缓冲区溢出。</p>
<h2 id="协程中的背压"><a href="#协程中的背压" class="headerlink" title="协程中的背压"></a>协程中的背压</h2><p>协程确实也提供了相同的缓冲和丢弃功能。 协程的基类是<code>Flow</code>。</p>
<p><img src="/posts/dc1362d2/flow-8897292.svg" alt="Coroutines' Flow class diagram excerpt"></p>
<p>你可以这样使用该类：</p>
<pre class=" language-java"><code class="language-java">flow <span class="token punctuation">{</span>                                  <span class="token comment" spellcheck="true">// 1</span>
  <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token function">emit</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>      <span class="token comment" spellcheck="true">// 2</span>
<span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">buffer</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span>                            <span class="token comment" spellcheck="true">// 3</span></code></pre>
<ol>
<li>创建一个<code>Flow</code>，其内容由后面的块定义</li>
<li>定义<code>Flow</code>的内容</li>
<li>设置缓冲区容量为10</li>
</ol>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>总而言之，RxJava，Project Reactor和Kotlin协程都提供背压功能。 所有用户都通过提供两种策略来应对生产速度比其订阅者消费速度更快的生产者：要么缓存数据项，要么删除数据项。</p>
<h3 id="参考："><a href="#参考：" class="headerlink" title="参考："></a>参考：</h3><ul>
<li><a target="_blank" rel="noopener" href="https://github.com/reactive-streams/reactive-streams-jvm">Reactive Streams JVM specifications</a></li>
<li><a target="_blank" rel="noopener" href="https://blog.softwaremill.com/how-not-to-use-reactive-streams-in-java-9-7a39ea9c2cb3">How (not) to use Reactive Streams in Java 9+</a></li>
<li><a target="_blank" rel="noopener" href="https://github.com/ReactiveX/RxJava/blob/3.x/docs/Backpressure.md">RxJava Backpressure</a></li>
</ul>

            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        文章作者:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="/about" rel="external nofollow noreferrer">Guo Yaxiang</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        文章链接:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://www.codinghonest.space/posts/dc1362d2.html">https://www.codinghonest.space/posts/dc1362d2.html</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        版权声明:
                    </i>
                </span>
                <span class="reprint-info">
                    本博客所有文章除特別声明外，均采用
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    许可协议。转载请注明来源
                    <a href="/about" target="_blank">Guo Yaxiang</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>复制成功，请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            
                                <a href="/tags/Java-Stream/">
                                    <span class="chip bg-color">Java Stream</span>
                                </a>
                            
                                <a href="/tags/Reactive-Stream/">
                                    <span class="chip bg-color">Reactive Stream</span>
                                </a>
                            
                                <a href="/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/">
                                    <span class="chip bg-color">响应式编程</span>
                                </a>
                            
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">
<div id="article-share">

    
    <div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
                <style>
    #reward {
        margin: 40px 0;
        text-align: center;
    }

    #reward .reward-link {
        font-size: 1.4rem;
        line-height: 38px;
    }

    #reward .btn-floating:hover {
        box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 5px 15px rgba(0, 0, 0, 0.2);
    }

    #rewardModal {
        width: 320px;
        height: 350px;
    }

    #rewardModal .reward-title {
        margin: 15px auto;
        padding-bottom: 5px;
    }

    #rewardModal .modal-content {
        padding: 10px;
    }

    #rewardModal .close {
        position: absolute;
        right: 15px;
        top: 15px;
        color: rgba(0, 0, 0, 0.5);
        font-size: 1.3rem;
        line-height: 20px;
        cursor: pointer;
    }

    #rewardModal .close:hover {
        color: #ef5350;
        transform: scale(1.3);
        -moz-transform:scale(1.3);
        -webkit-transform:scale(1.3);
        -o-transform:scale(1.3);
    }

    #rewardModal .reward-tabs {
        margin: 0 auto;
        width: 210px;
    }

    .reward-tabs .tabs {
        height: 38px;
        margin: 10px auto;
        padding-left: 0;
    }

    .reward-content ul {
        padding-left: 0 !important;
    }

    .reward-tabs .tabs .tab {
        height: 38px;
        line-height: 38px;
    }

    .reward-tabs .tab a {
        color: #fff;
        background-color: #ccc;
    }

    .reward-tabs .tab a:hover {
        background-color: #ccc;
        color: #fff;
    }

    .reward-tabs .wechat-tab .active {
        color: #fff !important;
        background-color: #22AB38 !important;
    }

    .reward-tabs .alipay-tab .active {
        color: #fff !important;
        background-color: #019FE8 !important;
    }

    .reward-tabs .reward-img {
        width: 210px;
        height: 210px;
    }
</style>

<div id="reward">
    <a href="#rewardModal" class="reward-link modal-trigger btn-floating btn-medium waves-effect waves-light red">赏</a>

    <!-- Modal Structure -->
    <div id="rewardModal" class="modal">
        <div class="modal-content">
            <a class="close modal-close"><i class="fas fa-times"></i></a>
            <h4 class="reward-title">你的赏识是我前进的动力</h4>
            <div class="reward-content">
                <div class="reward-tabs">
                    <ul class="tabs row">
                        <li class="tab col s6 alipay-tab waves-effect waves-light"><a href="#alipay">支付宝</a></li>
                        <li class="tab col s6 wechat-tab waves-effect waves-light"><a href="#wechat">微 信</a></li>
                    </ul>
                    <div id="alipay">
                        <img src="/medias/reward/alipay.png" class="reward-img" alt="支付宝打赏二维码">
                    </div>
                    <div id="wechat">
                        <img src="/medias/reward/wechat.png" class="reward-img" alt="微信打赏二维码">
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    $(function () {
        $('.tabs').tabs();
    });
</script>

            
        </div>
    </div>

    

    

    

    

    

    
        <style>
    .mvaline-card {
        margin: 1.5rem auto;
    }

    .mvaline-card .card-content {
        padding: 20px 20px 5px 20px;
    }
</style>

<div class="card mvaline-card" data-aos="fade-up">
    <div class="comment_headling" style="font-size: 20px; font-weight: 700; position: relative; padding-left: 20px; top: 15px; padding-bottom: 5px;">
        <i class="fas fa-comments fa-fw" aria-hidden="true"></i>
        <span>评论</span>
    </div>
    <div id="mvcomments" class="card-content" style="display: grid">
    </div>
</div>

<script src="/libs/minivaline/MiniValine.js"></script>
<script>
    new MiniValine({
        el: '#mvcomments',
        appId: 'oqAo7rCWejwTqHjGC2APTVDX-MdYXbMMI',
        appKey: 'pfyY919tO7iJaDm7lFmvh5Wp',
        mode: 'DesertsP',
        placeholder: 'Write a Comment',
        pathname: window.location.pathname,
        lang: '',
        adminEmailMd5: '8BBC105436DAAF8C803841C8FD13CD0C',
        tagMeta: ["管理员", "小伙伴", "访客"],
        master: ["8BBC105436DAAF8C803841C8FD13CD0C"],
        friends: ["FA21C7921CD42FE9523E957870873F64", "586BAB41BB5692877F7714DFE4947DEC"],
        math: true,
        md: true,
        enableQQ: false,
        NoRecordIP: false,
        visitor: true,
        maxNest: 6,
        pageSize: 6,
        serverURLs: '',
        emoticonUrl: ["https://cdn.jsdelivr.net/npm/alus@latest", "https://cdn.jsdelivr.net/gh/MiniValine/qq@latest", "https://cdn.jsdelivr.net/gh/MiniValine/Bilibilis@latest", "https://cdn.jsdelivr.net/gh/MiniValine/tieba@latest", "https://cdn.jsdelivr.net/gh/MiniValine/twemoji@latest", "https://cdn.jsdelivr.net/gh/MiniValine/weibo@latest"],
    });
</script>

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;上一篇</div>
            <div class="card">
                <a href="/posts/b23f3fc.html">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/2.jpg" class="responsive-img" alt="MapStruct使用指南">
                        
                        <span class="card-title">MapStruct使用指南</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            在本文中，我们探讨了MapStruct——一个用于创建映射器类的库。从基本映射到自定义方法和自定义映射器，此外， 我们还介绍了MapStruct提供的一些高级操作选项，包括依赖注入，数据类型映射、枚举映射和表达式使用。
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="far fa-clock fa-fw icon-date"></i>2021-04-30
                        </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/Java/" class="post-category">
                                    Java
                                </a>
                            
                            <a href="/categories/Java/%E6%95%88%E7%8E%87%E5%B7%A5%E5%85%B7/" class="post-category">
                                    效率工具
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/Java/">
                        <span class="chip bg-color">Java</span>
                    </a>
                    
                    <a href="/tags/%E6%95%88%E7%8E%87%E5%B7%A5%E5%85%B7/">
                        <span class="chip bg-color">效率工具</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                下一篇&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/posts/9e1d4cec.html">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/0.jpg" class="responsive-img" alt="实现Raft协议：Part 3 - 持久性和优化">
                        
                        <span class="card-title">实现Raft协议：Part 3 - 持久性和优化</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            本系列文章的写作目的，在于描述Raft协议的一个功能完备且经过严格测试的实现方式，并提供一些Raft工作方式的直观理解。
                        
                    </div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="far fa-clock fa-fw icon-date"></i>2021-03-04
                            </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/%E5%88%86%E5%B8%83%E5%BC%8F/" class="post-category">
                                    分布式
                                </a>
                            
                            <a href="/categories/%E5%88%86%E5%B8%83%E5%BC%8F/%E4%B8%80%E8%87%B4%E6%80%A7%E5%8D%8F%E8%AE%AE/" class="post-category">
                                    一致性协议
                                </a>
                            
                            <a href="/categories/%E5%88%86%E5%B8%83%E5%BC%8F/%E4%B8%80%E8%87%B4%E6%80%A7%E5%8D%8F%E8%AE%AE/Raft/" class="post-category">
                                    Raft
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%80%E8%87%B4%E6%80%A7/">
                        <span class="chip bg-color">分布式一致性</span>
                    </a>
                    
                    <a href="/tags/Raft/">
                        <span class="chip bg-color">Raft</span>
                    </a>
                    
                    <a href="/tags/%E8%AF%91%E6%96%87/">
                        <span class="chip bg-color">译文</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>


<script>
    $('#articleContent').on('copy', function (e) {
        // IE8 or earlier browser is 'undefined'
        if (typeof window.getSelection === 'undefined') return;

        var selection = window.getSelection();
        // if the selection is short let's not annoy our users.
        if (('' + selection).length < Number.parseInt('120')) {
            return;
        }

        // create a div outside of the visible area and fill it with the selected text.
        var bodyElement = document.getElementsByTagName('body')[0];
        var newdiv = document.createElement('div');
        newdiv.style.position = 'absolute';
        newdiv.style.left = '-99999px';
        bodyElement.appendChild(newdiv);
        newdiv.appendChild(selection.getRangeAt(0).cloneContents());

        // we need a <pre> tag workaround.
        // otherwise the text inside "pre" loses all the line breaks!
        if (selection.getRangeAt(0).commonAncestorContainer.nodeName === 'PRE') {
            newdiv.innerHTML = "<pre>" + newdiv.innerHTML + "</pre>";
        }

        var url = document.location.href;
        newdiv.innerHTML += '<br />'
            + '来源: 码道诚公<br />'
            + '文章作者: Guo Yaxiang<br />'
            + '文章链接: <a href="' + url + '">' + url + '</a><br />'
            + '本文章著作权归作者所有，任何形式的转载都请注明出处。';

        selection.selectAllChildren(newdiv);
        window.setTimeout(function () {bodyElement.removeChild(newdiv);}, 200);
    });
</script>


<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/libs/codeBlock/codeBlockFuction.js"></script>

<!-- 代码语言 -->

<script type="text/javascript" src="/libs/codeBlock/codeLang.js"></script>


<!-- 代码块复制 -->

<script type="text/javascript" src="/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/libs/codeBlock/codeShrink.js"></script>


<!-- 代码块折行 -->

<style type="text/css">
code[class*="language-"], pre[class*="language-"] { white-space: pre !important; }
</style>


    

</main>




    <footer class="page-footer bg-color">
    
        <link rel="stylesheet" href="/libs/aplayer/APlayer.min.css">
<style>
    .aplayer .aplayer-lrc p {
        
        display: none;
        
        font-size: 12px;
        font-weight: 700;
        line-height: 16px !important;
    }

    .aplayer .aplayer-lrc p.aplayer-lrc-current {
        
        display: none;
        
        font-size: 15px;
        color: #42b983;
    }

    
    .aplayer.aplayer-fixed.aplayer-narrow .aplayer-body {
        left: -66px !important;
    }

    .aplayer.aplayer-fixed.aplayer-narrow .aplayer-body:hover {
        left: 0px !important;
    }

    
</style>
<div class="">
    
    <div class="row">
        <meting-js class="col l8 offset-l2 m10 offset-m1 s12"
                   server="netease"
                   type="playlist"
                   id="503838841"
                   fixed='true'
                   autoplay='false'
                   theme='#42b983'
                   loop='all'
                   order='random'
                   preload='auto'
                   volume='0.7'
                   list-folded='true'
        >
        </meting-js>
    </div>
</div>

<script src="/libs/aplayer/APlayer.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script>

    
    <div class="container row center-align" style="margin-bottom: 0px !important;">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            
                <span id="year">2020-2021</span>
            
            <span id="year">2020</span>
            <a href="/about" target="_blank">Guo Yaxiang</a>
            |&nbsp;Powered by&nbsp;<a href="https://hexo.io/" target="_blank">Hexo</a>
            |&nbsp;Theme&nbsp;<a href="https://github.com/blinkfox/hexo-theme-matery" target="_blank">Matery</a>
            <br>
            
            
            
            
            
            <br>
            
            <br>
            
        </div>
        <div class="col s12 m4 l4 social-link ">
    <a href="https://github.com/GuoYaxiang" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>



    <a href="mailto:YXGyxg112358@163.com" class="tooltipped" target="_blank" data-tooltip="邮件联系我" data-position="top" data-delay="50">
        <i class="fas fa-envelope-open"></i>
    </a>







    <a href="tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=1431800679" class="tooltipped" target="_blank" data-tooltip="QQ联系我: 1431800679" data-position="top" data-delay="50">
        <i class="fab fa-qq"></i>
    </a>





    <a href="https://www.zhihu.com/people/guo-ya-xiang" class="tooltipped" target="_blank" data-tooltip="关注我的知乎: https://www.zhihu.com/people/guo-ya-xiang" data-position="top" data-delay="50">
        <i class="fab fa-zhihu1">知</i>
    </a>



    <a href="/atom.xml" class="tooltipped" target="_blank" data-tooltip="RSS 订阅" data-position="top" data-delay="50">
        <i class="fas fa-rss"></i>
    </a>

</div>
    </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;搜索</span>
            <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script src="/js/search.js"></script>
<script type="text/javascript">
$(function () {
    searchFunc('/search.xml', 'searchInput', 'searchResult');
});
</script>

    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>


    <script src="/libs/materialize/materialize.min.js"></script>
    <script src="/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/libs/aos/aos.js"></script>
    <script src="/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/js/matery.js"></script>

    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    <script src="/libs/others/clicklove.js" async="async"></script>
    
    

    
        <script src="//code.tidio.co/zgiqinzrat1rzwxseqhr2pnzsp2a4rjo.js"></script>
        <script>
            $(document).ready(function () {
                setInterval(change_Tidio, 50);
                function change_Tidio() {
                    var tidio=$("#tidio-chat iframe");
                    if(tidio.css("display")=="block"&& $(window).width()>977 ){
                        document.getElementById("tidio-chat-iframe").style.bottom= ($("div#backTop.top-scroll").css("display")=="none" &&$(window).width()>977)>0? "-40px" : ($("div.toc-title").length&&$(window).width()>977)>0?"85px":"20px";
                        document.getElementById("tidio-chat-iframe").style.right="-15px";
                        document.getElementById("tidio-chat-iframe").style.height=parseInt(tidio.css("height"))>=520?"520px":tidio.css("height");
                        document.getElementById("tidio-chat-iframe").style.zIndex="997";
                    }
                    else if(tidio.css("display")=="block"&&$(window).width()>601 &&$(window).width()<992 ){
                        document.getElementById("tidio-chat-iframe").style.bottom= ($("div#backTop.top-scroll").css("display")=="none" && 601< $(window).width()<992)>0? "-40px":"20px" ;
                        document.getElementById("tidio-chat-iframe").style.right="-15px";
                        document.getElementById("tidio-chat-iframe").style.zIndex="997";
                    }
                    else if(tidio.css("display")=="block"&&$(window).width()<601 && parseInt(tidio.css("height"))<230){
                        document.getElementById("tidio-chat-iframe").style.bottom= ($("div#backTop.top-scroll").css("display")=="none" && $(window).width()<601)>0? "-10px":"45px" ;
                        document.getElementById("tidio-chat-iframe").style.zIndex="997";
                    }
                    if( tidio.css("display")=="block"&&$(window).width()<601 && parseInt(tidio.css("height"))>=230){
                        document.getElementById("tidio-chat-iframe").style.zIndex="998";
                    }
                }
            });
        </script>
    

    

    

    

    

    
    <script src="/libs/instantpage/instantpage.js" type="module"></script>
    

</body>

</html>
